"use strict";

const fs = require("fs");
const path = require("path");
const inquirer = require("inquirer");
const fse = require("fs-extra");
const semver = require("semver");
const { spinnerStart, sleep,execAsync } = require("@dbc-dev/utils");
const userHome = require("user-home");
const ejs = require('ejs');
const glob = require('glob');
const Command = require("@dbc-dev/command");
const Package = require("@dbc-dev/package");
const log = require("@dbc-dev/log");

const getProjectTemplate = require("./getProjectTemplate");
const TYPE_PROJECT = "project";
const TYPE_COMPONENT = "component";
const TEMPLATE_TYPE_NORMAL='normal';
const TEMPLATE_TYPE_CUSTOM='custom';

const WHITE_COMMAND=['npm','cnpm'];
const COMPONENT_FILE = '.componentrc';
class InitCommand extends Command {
  init() {
    console.log(process.env.DBC_BASE_URL);
    this.projectName = this._argv[0] || "";
    this.force = !!this._cmd.force;
    log.verbose("projectName", this.projectName);
    log.verbose("force", this.force);
  }
  async exec() {
    try {
      const projectInfo = await this.prepare();
      if (projectInfo) {
        this.projectInfo = projectInfo;
        await this.downloadTemplate();
        await this.installTemplate();
      }
    } catch (e) {
      log.error(e.message);
    }
  }
  async installTemplate(){
    if(this.templateInfo){
      if(!this.templateInfo.type){
        this.templateInfo.type = TEMPLATE_TYPE_NORMAL;
      }
      if(this.templateInfo.type == TEMPLATE_TYPE_NORMAL){
        this.installNormalTemplate();
      }else if(this.templateInfo.type == TEMPLATE_TYPE_CUSTOM){
        this.installCustomTemplate();
      }else{
        throw new Error('项目模板类型无法识别')
      }
    }else{
      throw new Error('项目模板信息不存在')
    }
  }
  checkCommand(cmd){
    if(WHITE_COMMAND.includes(cmd)){
      return cmd;
    }
    return null
  }
  async execCommand(command,errMsg){
    let ret;
    if(command){
      const cmdArray = command.split(' ');
      const cmd = this.checkCommand(cmdArray[0]);
      if(!cmd){
        throw new Error('命令不存在,命令：',command)
      }
      const args = cmdArray.slice(1);
      ret = await execAsync(cmd,args,{
        stdio:'inherit',
        cwd:process.cwd()
      })
      console.log(ret,cmd,args,process.cwd(),'啊哈哈哈哈哈啊啊啊啊');
      if(ret!==0){
       
        throw new Error(errMsg);
      }
      return ret;
    }
  }
  async ejsRender(options) {
    const dir = process.cwd();
    const projectInfo = this.projectInfo;
    return new Promise((resolve, reject) => {
      glob('**', {
        cwd: dir,
        ignore: options.ignore || '',
        nodir: true,
      }, function(err, files) {
        if (err) {
          reject(err);
        }
        Promise.all(files.map(file => {
          const filePath = path.join(dir, file);
          return new Promise((resolve1, reject1) => {
            ejs.renderFile(filePath, projectInfo, {}, (err, result) => {
              if (err) {
                reject1(err);
              } else {
                fse.writeFileSync(filePath, result);
                resolve1(result);
              }
            });
          });
        })).then(() => {
          resolve();
        }).catch(err => {
          reject(err);
        });
      });
    });
  }
  async installNormalTemplate(){
    //拷贝模板至当前目录
    let spinner = spinnerStart('正在安装模板');
    await sleep();
    const targetPath = process.cwd();
    try{
      const templatePath = path.resolve(this.templateNpm.cacheFilePath,'template');
      const targetPath = process.cwd();
      fse.ensureDirSync(templatePath);
      fse.ensureDirSync(targetPath);
      fse.copySync(templatePath,targetPath);
    }catch(e){
      throw e;
    }finally{
      spinner.stop(true);
      log.success('模板安装成功')
    }
    const templateIgnore = this.templateInfo.ignore || [];
    const ignore = ['**/node_modules/**', ...templateIgnore];
    await this.ejsRender({ ignore });
    // 如果是组件，则生成组件配置文件
    await this.createComponentFile(targetPath);
    //依赖安装
    const {installCommand,startCommand} = this.templateInfo;
    await this.execCommand(installCommand,'依赖安装失败');
    //启动命令执行
    await this.execCommand(startCommand,'启动执行命令失败！');
    
  }
  async createComponentFile(targetPath) {
    const templateInfo = this.templateInfo;
    const projectInfo = this.projectInfo;
    if (templateInfo.tag.includes(TYPE_COMPONENT)) {
      const componentData = {
        ...projectInfo,
        buildPath: templateInfo.buildPath,
        examplePath: templateInfo.examplePath,
        npmName: templateInfo.npmName,
        npmVersion: templateInfo.version,
      };
      const componentFile = path.resolve(targetPath, COMPONENT_FILE);
      fs.writeFileSync(componentFile, JSON.stringify(componentData));
    }
  }
  async installCustomTemplate(){
   // 查询自定义模板的入口文件
   if (await this.templateNpm.exists()) {
    const rootFile = this.templateNpm.getRootFilePath();
    console.log(rootFile,'an2');
    if (fs.existsSync(rootFile)) {
      log.notice('开始执行自定义模板');
      const templatePath = path.resolve(this.templateNpm.cacheFilePath,'template');
      const options = {
        templateInfo: this.templateInfo,
        projectInfo: this.projectInfo,
        sourcePath: templatePath,
        targetPath: process.cwd(),
      };
      const code = `require('${rootFile}')(${JSON.stringify(options)})`;
      log.verbose('code', code);
      await execAsync('node', ['-e', code], { stdio: 'inherit', cwd: process.cwd() });
      log.success('自定义模板安装成功');
    } else {
      throw new Error('自定义模板入口文件不存在！');
    }
  }
  }
  async downloadTemplate() {
    const { projectTemplate } = this.projectInfo;
    const templateInfo = this.template.find(
      (item) => item.npmName === projectTemplate
    );
    const targetPath = path.resolve(userHome, ".dbc-dev", "template");
    const storeDir = path.resolve(
      userHome,
      ".dbc-dev",
      "template",
      "node_modules"
    );
    const { npmName, version } = templateInfo;
    this.templateInfo = templateInfo;
    const templateNpm = new Package({
      targetPath,
      storeDir,
      packageName: npmName,
      packageVersion: version,
    });
    const a = await templateNpm.exists();
    if (!(await templateNpm.exists())) {
      const spinner = spinnerStart("正在下载模板...");
      await sleep();
      try{
        await templateNpm.install();
      }catch(e){
        throw e;
      }finally{
        spinner.stop(true);
        if(templateNpm.exists()){
          log.success("下载模板成功");
          this.templateNpm = templateNpm;
        }
      }
    } else {
      const spinner = spinnerStart("正在更新模板...");
      await sleep();
      try{
        await templateNpm.update();
      }catch(e){
        throw e;
      }finally{
        spinner.stop(true);
        if(templateNpm.exists()){
          log.success("更新模板成功");
          this.templateNpm = templateNpm;
        }
      }
     
      
     
    }
  }
  async prepare() {
    const template = await getProjectTemplate();

    if (!template || template.length == 0) {
      throw new Error("项目模板不存在");
    }
    this.template = template;
    const localPath = process.cwd();
    if (!this.isDirEmpty(localPath)) {
      let ifContinue = false;
      console.log(this.force);
      if (!this.force) {
        ifContinue = (
          await inquirer.prompt({
            type: "confirm",
            name: "ifContinue",
            default: false,
            message: "当前文件夹不为空，是否继续创建项目？",
          })
        ).ifContinue;
        if (!ifContinue) {
          return;
        }
      }

      if (ifContinue || this.force) {
        const { confirmDelete } = await inquirer.prompt({
          type: "confirm",
          name: "confirmDelete",
          default: false,
          message: "是否清空当前文件夹？",
        });
        if (confirmDelete) {
          fse.emptyDirSync(localPath);
        }
      }
    }

    return this.getProjectInfo();
  }
  async getProjectInfo() {
    function isValidName(v){
      return /^(@[a-zA-Z0-9-_]+\/)?[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(v)
    }
    let projectInfo = {};
    let isProjectNameValid = false;


    console.log(this.projectName,'duanbc')
    if(isValidName(this.projectName)){
      isProjectNameValid = true;
      projectInfo.projectName = this.projectName;
    }
    // 1. 选择创建项目或组件
    const { type } = await inquirer.prompt({
      type: "list",
      name: "type",
      message: "请选择初始化类型",
      default: TYPE_PROJECT,
      choices: [
        {
          name: "项目",
          value: TYPE_PROJECT,
        },
        {
          name: "组件",
          value: TYPE_COMPONENT,
        },
      ],
    });
    this.template = this.template.filter(template =>  template.tag.includes(type))
    const title = type === TYPE_PROJECT ? '项目' : '组件';
    
      //获取项目基本信息
      const projectNamePromt = {
          type: "input",
          name: "projectName",
          message:  `请输入${title}名称`,
          default: "",
          validate: function (v) {
            const done = this.async();
            setTimeout(function() {
          // 1.首字符必须为英文字符
          // 2.尾字符必须为英文或数字，不能为字符
          // 3.字符仅允许"-_"
            if (!isValidName(v)) {
              done(`请输入合法的${title}名称`);
              return;
            }
            done(null, true);
          }, 0);
          },
          filter: function (v) {
            return v;
          },
        };
        const projectPrompt = [];
        if(!isProjectNameValid){
          projectPrompt.push(projectNamePromt)
        }
        projectPrompt.push({
          type: "input",
          name: "projectVersion",
          message: `请输入${title}版本号`,
          default: "1.0.0",
          validate: function (v) {
            const done = this.async();
            setTimeout(function() {
              if (!(!!semver.valid(v))) {
                done('请输入合法的版本号');
                return;
              }
              done(null, true);
            }, 0);
          },
          filter: function (v) {
            if (!!semver.valid(v)) {
              return semver.valid(v);
            } else {
              return v;
            }
          },
        },
        {
          type: "list",
          name: "projectTemplate",
          message: `请选择${title}模板`,
          choices: this.createTemplateChoice(),
        });
    if (type == TYPE_PROJECT) {
      const project = await inquirer.prompt(projectPrompt);
      projectInfo = {
        ...projectInfo,
        type,
        ...project,
      };
    } else if (type == TYPE_COMPONENT) {
      const descriptionPrompt = {
        type: 'input',
        name: 'componentDescription',
        message: '请输入组件描述信息',
        default: '',
        validate: function(v) {
          const done = this.async();
          setTimeout(function() {
            if (!v) {
              done('请输入组件描述信息');
              return;
            }
            done(null, true);
          }, 0);
        },
      };
      projectPrompt.push(descriptionPrompt);
      // 2. 获取组件的基本信息
      const component = await inquirer.prompt(projectPrompt);
      projectInfo = {
        ...projectInfo,
        type,
        ...component,
      };
    }
    if(projectInfo.projectName){
      projectInfo.name = projectInfo.projectName;
      projectInfo.className = require('kebab-case')(projectInfo.projectName).replace(/^-/,'');
    }
    if (projectInfo.projectVersion) {
      projectInfo.version = projectInfo.projectVersion;
    }
    if (projectInfo.componentDescription) {
      projectInfo.description = projectInfo.componentDescription;
    }
    return projectInfo;
  }
  isDirEmpty(localPath) {
    let fileList = fs.readdirSync(localPath);
    fileList = fileList.filter(
      (file) => !file.startsWith(".") && ["node_modules"].indexOf(file) < 0
    );
    return !fileList || fileList.length <= 0;
  }
  createTemplateChoice() {
    return this.template.map((item) => ({
      value: item.npmName,
      name: item.name,
    }));
  }
  
}
function init(argv) {
  return new InitCommand(argv);
}
module.exports = init;
module.exports.InitCommand = InitCommand;
